export class FastBitSet {
    private bits: Uint32Array;
    private capacity: number;

    constructor(size: number) {
        this.capacity = size;
        const wordCount = Math.ceil(size / 32);
        this.bits = new Uint32Array(wordCount);
    }

    private getWordIndex(index: number): number {
        return index >>> 5; // index / 32
    }

    private getBitMask(index: number): number {
        return 1 << (index & 31); // index % 32
    }

    set(index: number, value: boolean): void {
        const word = this.getWordIndex(index);
        const mask = this.getBitMask(index);
        if (value) {
            this.bits[word] |= mask;
        } else {
            this.bits[word] &= ~mask;
        }
    }

    get(index: number): boolean {
        const word = this.getWordIndex(index);
        const mask = this.getBitMask(index);
        return (this.bits[word] & mask) !== 0;
    }

    clear(): void {
        this.bits.fill(0);
    }

    size(): number {
        return this.capacity;
    }

    count(): number {
        let total = 0;
        for (const word of this.bits) {
            total += this.popCount(word);
        }
        return total;
    }

    private popCount(x: number): number {
        // Hamming weight (Brian Kernighan's method)
        let count = 0;
        while (x) {
            x &= x - 1;
            count++;
        }
        return count;
    }

    print(): void {
        const result: number[] = [];
        for (let i = 0; i < this.capacity; i++) {
            if (this.get(i)) result.push(i);
        }
        console.log("Set bits: ", result.join(", "));
    }
}